/************************************************************************************
   Copyright (C) 2020,2023 MariaDB Corporation AB

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not see <http://www.gnu.org/licenses>
   or write to the Free Software Foundation, Inc.,
   51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/


#ifndef _MARIADBFUNCTIONSTATEMENT_H_
#define _MARIADBFUNCTIONSTATEMENT_H_

#include "Consts.h"

#include "SelectResultSet.h"
#include "parameters/ParameterHolder.h"

namespace sql
{
namespace mariadb
{
class SelectResultSet;
class ParameterHolder;
class CallParameter;
class MariaDbConnection;

class MariaDbFunctionStatement  : public  CloneableCallableStatement
{
  SelectResultSet* outputResultSet= nullptr;
  Unique::ClientSidePreparedStatement stmt;
  Shared::CallableParameterMetaData parameterMetadata;
  MariaDbConnection* connection;
  std::vector<CallParameter> params;
  SQLString databaseName;
  SQLString functionName;

  MariaDbFunctionStatement(const MariaDbFunctionStatement &other, MariaDbConnection* connection);
public:
  MariaDbFunctionStatement(
    MariaDbConnection* connection,
    const SQLString& databaseName, const SQLString& procedureName, const SQLString& arguments,
    int32_t resultSetType,
    int32_t resultSetConcurrency,
    Shared::ExceptionFactory& exptnFactory);
protected:
  SelectResultSet* getResult();
  SelectResultSet* getOutputResult();
public:
  MariaDbFunctionStatement* clone(MariaDbConnection* connection);
  int32_t executeUpdate();
private:
  //TODO: These, some of, and maybe some other methods are common with Procedure class. Along with related properties
  // Should be moved to separate class at some point, and Procedure and Function classes will use them.
  void retrieveOutputResult();
  int32_t indexToOutputIndex(uint32_t parameterIndex);
  int32_t nameToOutputIndex(const SQLString& parameterName);
  int32_t nameToIndex(const SQLString& parameterName);
  CallParameter& getParameter(uint32_t index);
  Shared::Results& getResults();
  void readMetadataFromDbIfRequired();

public:
  void initFunctionData(int32_t parametersCount);
  void setParameter(int32_t parameterIndex, ParameterHolder& holder);
  ResultSet* executeQuery();
  bool execute();
  Connection* getConnection();
  ParameterMetaData* getParameterMetaData();

  //Call forwarders to ClientSidePreparedStatement
  bool execute(const sql::SQLString &sql, const sql::SQLString *colNames);
  bool execute(const sql::SQLString &sql, int32_t *colIdxs);
  bool execute(const SQLString& sql);
  bool execute(const SQLString& sql, int32_t autoGeneratedKeys);
  ResultSet* executeQuery(const SQLString& sql);
  int64_t executeLargeUpdate(const SQLString& sql);
  int64_t executeLargeUpdate(const SQLString& sql, int32_t autoGeneratedKeys);
  int64_t executeLargeUpdate(const SQLString& sql, int32_t* columnIndexes);
  int64_t executeLargeUpdate(const SQLString& sql, const SQLString* columnNames);
  int32_t executeUpdate(const SQLString& sql);
  int32_t executeUpdate(const SQLString& sql, int32_t autoGeneratedKeys);
  int32_t executeUpdate(const SQLString& sql, int32_t* columnIndexes);
  int32_t executeUpdate(const SQLString& sql, const SQLString* columnNames);
  uint32_t getMaxFieldSize();
  void setMaxFieldSize(uint32_t max);
  int32_t getMaxRows();
  void setMaxRows(int32_t max);
  int64_t getLargeMaxRows();
  void setLargeMaxRows(int64_t max);
  void setEscapeProcessing(bool enable);
  int32_t getQueryTimeout();
  void setQueryTimeout(int32_t seconds);
  void cancel();
  SQLWarning* getWarnings();
  void clearWarnings();
  void setCursorName(const SQLString& name);
  ResultSet* getGeneratedKeys();
  int32_t getResultSetHoldability();
  bool isClosed();
  bool isPoolable();
  void setPoolable(bool poolable);
  ResultSet* getResultSet();
  int32_t getUpdateCount();
  int64_t getLargeUpdateCount();
  bool getMoreResults();
  bool getMoreResults(int32_t current);
  int32_t getFetchDirection();
  void setFetchDirection(int32_t direction);
  int32_t getFetchSize();
  void setFetchSize(int32_t rows);
  int32_t getResultSetConcurrency();
  int32_t getResultSetType();
  void closeOnCompletion();
  bool isCloseOnCompletion();
  void addBatch();
  void addBatch(const SQLString& sql);
  void clearBatch();
  void close();
  int64_t executeLargeUpdate();
  void registerOutParameter(int32_t parameterIndex, int32_t sqlType, const SQLString& typeName);
  void registerOutParameter(int32_t parameterIndex, int32_t sqlType);
  void registerOutParameter(int32_t parameterIndex, int32_t sqlType, int32_t scale);
  void registerOutParameter(const SQLString& parameterName, int32_t sqlType);
  void registerOutParameter(const SQLString& parameterName, int32_t sqlType, int32_t scale);
  void registerOutParameter(const SQLString& parameterName, int32_t sqlType, const SQLString& typeName);

  const sql::Ints& executeBatch();
  const sql::Longs& executeLargeBatch();

  bool wasNull();
  SQLString getString(int32_t parameterIndex);
  SQLString getString(const SQLString& parameterName);
  bool getBoolean(int32_t parameterIndex);
  bool getBoolean(const SQLString& parameterName);
  int8_t getByte(int32_t parameterIndex);
  int8_t getByte(const SQLString& parameterName);
  int16_t getShort(int32_t parameterIndex);
  int16_t getShort(const SQLString& parameterName);
  int32_t getInt(const SQLString& parameterName);
  int32_t getInt(int32_t parameterIndex);
  int64_t getLong(const SQLString& parameterName);
  int64_t getLong(int32_t parameterIndex);
  float getFloat(const SQLString& parameterName);
  float getFloat(int32_t parameterIndex);
  long double getDouble(int32_t parameterIndex);
  long double getDouble(const SQLString& parameterName);
  void setNull(int32_t parameterIndex, int32_t sqlType);
  void setNull(int32_t parameterIndex, int32_t sqlType, const SQLString& typeName);
  void setBlob(int32_t parameterIndex, std::istream* inputStream, const int64_t length);
  void setBlob(int32_t parameterIndex, std::istream* inputStream);
  void setBoolean(int32_t parameterIndex, bool value);
  void setByte(int32_t parameterIndex, int8_t byte);
  void setShort(int32_t parameterIndex, int16_t value);
  void setString(int32_t parameterIndex, const SQLString& str);
  void setBytes(int32_t parameterIndex, sql::bytes* bytes);
  void setInt(int32_t column, int32_t value);
  void setLong(int32_t parameterIndex, int64_t value);
  void setInt64(int32_t parameterIndex, int64_t value) { setLong(parameterIndex, value); }
  void setUInt64(int32_t parameterIndex, uint64_t value);
  void setUInt(int32_t parameterIndex, uint32_t value);
  void setFloat(int32_t parameterIndex, float value);
  void setDouble(int32_t parameterIndex, double value);
  void setDateTime(int32_t parameterIndex, const SQLString& dt);
  void setBigInt(int32_t column, const SQLString& value);

  void setNull(const SQLString& parameterName, int32_t sqlType);
  void setNull(const SQLString& parameterName, int32_t sqlType, const SQLString& typeName);
  void setBoolean(const SQLString& parameterName, bool boolValue);
  void setByte(const SQLString& parameterName, char byteValue);
  void setShort(const SQLString& parameterName, int16_t shortValue);
  void setInt(const SQLString& parameterName, int32_t intValue);
  void setLong(const SQLString& parameterName, int64_t longValue);
  void setInt64(const SQLString& parameterName, int64_t longValue) { setLong(parameterName, longValue); }
  void setFloat(const SQLString& parameterName, float floatValue);
  void setDouble(const SQLString& parameterName, double doubleValue);
  void setString(const SQLString& parameterName, const SQLString& stringValue);
  void setBytes(const SQLString& parameterName, sql::bytes* bytes);

  Statement* setResultSetType(int32_t rsType);
  void clearParameters();

#ifdef MAYBE_IN_NEXTVERSION
  std::istringstream* getCharacterStream(int32_t parameterIndex);
  std::istringstream* getCharacterStream(const SQLString& parameterName);
  void setBlob(const SQLString& parameterName, const std::istream* inputStream, int64_t length);
  void setBlob(const SQLString& parameterName, const std::istream* inputStream);
  sql::bytes* getBytes(const SQLString& parameterName);
  sql::bytes* getBytes(int32_t parameterIndex);
  Blob* getBlob(int32_t parameterIndex);
  Blob* getBlob(const SQLString& parameterName);
  Date getDate(int32_t parameterIndex);
  Date getDate(const SQLString& parameterName);
  void setBigDecimal(const SQLString& parameterName, const BigDecimal& bigDecimal);
#endif

};
}
}
#endif
